#!/usr/bin/env python
# -*- coding: utf-8 -*-
# 时间     : 2021-04-23 11:19
# 作者     : DL (584054432@qq.com)
# 网址     : https://gitee.com/dingliangPython/
# 软件版本 : Python3.7.3
# 功能     ：

# !/usr/bin/env python
# -*- coding: utf-8 -*-
# 时间     : 2021-04-20 15:51
# 作者     : DL (584054432@qq.com)
# 网址     : https://gitee.com/dingliangPython/
# 软件版本 : Python3.7.3
# 功能     ：
# cython: language_level=3
from ctypes import windll

import functools
import logging
import os, sys,time


import psutil
import win32con
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QAction, QInputDialog, QLineEdit, QMenu, QMessageBox, QSystemTrayIcon, QWidget
import loadConfig
from connect_database import Connect

log = logging.getLogger('main.common')

# 默认日志颜色
LOG_COLOR = {"10": "Silver", "20": "DarkGreen", "30": "Orange", "40": "red", "50": "DarkRed", }
# 程序名
APP_NAME = os.path.splitext(os.path.basename(sys.argv[0]))[0]
# 程序配置目录
APP_CONFIG_PATH = os.path.normpath(os.path.dirname(sys.argv[0]))
# 程序脚本目录
SCRIPTS_PATH = os.path.join(APP_CONFIG_PATH, 'Scripts')
APP_SCRIPTS_PATH = os.path.join(APP_CONFIG_PATH, APP_NAME, 'Scripts')
# 程序模块目录
MODULES_PATH = os.path.join(APP_CONFIG_PATH, 'Modules')
APP_MODULES_PATH = os.path.join(APP_CONFIG_PATH, APP_NAME, 'Modules')
# 日志目录
LOG_PATH = os.path.join(APP_CONFIG_PATH, 'log')

if not os.path.isdir('./' + APP_NAME):
    os.mkdir(APP_NAME)

if not os.path.isdir(APP_SCRIPTS_PATH):
    os.mkdir(APP_SCRIPTS_PATH)
sys.path.append(APP_SCRIPTS_PATH)

if not os.path.isdir(APP_MODULES_PATH):
    os.mkdir(APP_MODULES_PATH)
sys.path.append(APP_MODULES_PATH)

# 获取颜色
def get_log_color(msg):
    color = '#999999'
    if "INFO" in msg:
        return LOG_COLOR.get('20')
    if "DEBUG" in msg:
        return LOG_COLOR.get('10')
    if "WARNING" in msg:
        return LOG_COLOR.get('30')
    if "ERROR" in msg:
        return LOG_COLOR.get('40')
    return color

def run_time(func):
    begin_time = time.process_time()

    @functools.wraps(func)
    def wrapper(self, *args, **kwargs):
        if args:
            res = func(self, *args, **kwargs)
        else:
            res = func(self)
        return res

    log.warning("运行耗时：{}秒".format(time.process_time() - begin_time))
    return wrapper


# 校验密码
def password_check():
    password = "ygsoft.{}".format(time.strftime("%m%d", time.localtime(time.time())))
    value, ok = QInputDialog.getText(None, '密码验证', '输入密码：', echo = QLineEdit.PasswordEchoOnEdit)
    if not ok and value != password:
        msg = " * 密码不对?\n * 是不可能让你操作的!!!\n\n * 友情提示：每天都会变的哦 ^_^      "
        # QMessageBox.information(None, "What?", msg, QMessageBox.Yes)
        show_msg(msg,'What?')
        return
    if ok and value == password:
        return True


# 可写验证装饰器
def check_opc_write(func):
    def work(*args, **kwargs):
        local_config_obj = loadConfig.Config()
        config = local_config_obj.get_config()
        write_start = config.get('opc', {}).get('write')
        log = logging.getLogger('main.check_opc')
        if not write_start:
            log.warning('opc未启用写入功能,请在菜单配置里选择可写入....')
            return
        return func(*args, **kwargs)

    return work


# 验证密码装饰器
def check_user_password(func):
    def work(*args, **kwargs):
        local_config_obj = loadConfig.Config()
        config = local_config_obj.get_config()
        password_satrt = config.get('app', {}).get('password')
        if password_satrt and not password_check():
            return
        return func(*args, **kwargs)

    return work


# 验证密码装饰器
def check_local_db_password(db_path = None):
    def run(func):
        def work(*args, **kwargs):
            config_path = os.path.join(APP_CONFIG_PATH, APP_NAME, 'config.db') if not db_path else './config.db'
            config = {'databasepath': config_path, 'type': 'sqlite'}
            connect = Connect(**config)
            sql = "SELECT * FROM APPConfig"
            data = connect.loadSql(sql, 1)
            if not data:
                show_message("查找不到配置文件:{},退出程序...".format(config_path))
                os._exit(0)
            password_satrt = data[0].get('EnablePassword')
            if password_satrt and not password_check():
                return
            return func(*args, **kwargs)

        return work

    return run


# 提示信息
def show_message(info):
    result = QMessageBox.information(QWidget(), "提示", info, QMessageBox.Yes | QMessageBox.No)
    return True if result == QMessageBox.Yes else False

# window 提示
def show_msg(info, title = '提示'):
    result = windll.user32.MessageBoxA(0, info.encode('gbk'), title.encode('gbk'), 1 | win32con.MB_ICONWARNING)
    return True if result == 1 else False


# 异常错误信息装饰器
def error_info(func):
    def work(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except Exception as e:
            if 'Permission denied' in e.args:
                info = "执行函数:{},提示:{}".format(func.__name__, "请检测是否有其它程序占用配置文件或打开?")
            else:
                info = "执行函数:{},异常:{}".format(func.__name__, e)
            log.warning(info)
            show_message(info)

    return work


# 检测内存和cup
def check_cpu_memory(size = 30, sec = 3):
    # 核心数
    cpu_count = psutil.cpu_count(logical = True)
    # 进程
    process = psutil.Process(os.getpid())
    # cpu占用
    cpu_occupy = process.cpu_percent(interval = sec)
    # 得到进程CPU占用
    cpu = cpu_occupy / cpu_count
    # 内存占用率
    memory = process.memory_percent()
    if cpu >= size or memory >= size:
        log.info("程序运行，CPU占用率：%{}，或内存占用率：%{} 超过{}%，10秒后即将退出程序！！！".format(cpu, memory, size))
        for i in range(10):
            time.sleep(1)
            log.info("倒计时：{}秒".format(9 - i))
        os._exit(0)
    else:
        log.info("程序运行，CPU占用率：%{}，或内存占用率：%{} ".format(cpu, memory))


# 显示窗口在顶端
def show_window_top(window):
    window.activateWindow()
    window.setWindowState(window.windowState() & ~Qt.WindowMinimized | Qt.WindowActive)
    window.showNormal()


# 计算数值
def calc_value(value, mapping):
    try:
        if not mapping:
            return value
        if ':' in mapping and isinstance(eval(value), int):
            map_dict = dict(((v for v in value.split(':')) for value in mapping.split(',')))
            return map_dict.get(str(value), value)
        new_value = eval('{}{}'.format(value, mapping))
        if isinstance(eval(value), int):
            return str(int(new_value))
        else:
            return str(new_value)
    except Exception as e:
        log.warning("计算映射出错:{},请检测值:{},映射配置是否正确...".format(e, value))


# 系统托盘图标
class SystemTray(object):
    # 程序托盘类
    # noinspection PyShadowingNames
    def __init__(self, gui, password_enable = None):
        self.password_enable = password_enable
        self.gui = gui
        self.gui.show()  # 不设置显示则为启动最小化到托盘
        self.tp = QSystemTrayIcon(self.gui)
        # 设置托盘图标
        self.tp.setIcon(QIcon(":/icon/icon.ico"))
        self.set_menu()

    # 图标双击
    def act(self, reason):
        # 主界面显示方法
        # 鼠标点击icon传递的信号会带有一个整形的值，1是表示单击右键，2是双击，3是单击左键，4是用鼠标中键点击
        if reason == 2:
            if self.gui.isHidden():
                show_window_top(self.gui)
            else:
                self.gui.hide()
                show_window_top(self.gui)

    # 先隐藏图标后退出
    @check_user_password
    def quit(self, code = 0):
        self.tp.hide()
        os._exit(code)

    def set_menu(self):
        a1 = QAction(self.gui)
        a1.setText("最小化")
        a1.triggered.connect(self.gui.hide)

        a2 = QAction(self.gui)
        a2.setText("最大化")
        a2.triggered.connect(self.gui.showMaximized)

        a3 = QAction(self.gui)
        a3.setText("还原大小")
        a3.triggered.connect(lambda: show_window_top(self.gui))

        a4 = QAction(self.gui)
        a4.setText("打开日志")
        a4.triggered.connect(self.gui.open_log_file)

        a5 = QAction(self.gui)
        a5.setText("退出程序")
        a5.triggered.connect(self.quit)

        tpMenu = QMenu()
        tpMenu.addAction(a1)
        tpMenu.addSeparator()
        tpMenu.addAction(a2)
        tpMenu.addSeparator()
        tpMenu.addAction(a3)
        tpMenu.addSeparator()
        tpMenu.addAction(a4)
        tpMenu.addSeparator()
        tpMenu.addAction(a5)
        self.tp.setContextMenu(tpMenu)
        self.tp.setToolTip(self.gui.windowTitle())
        self.tp.show()  # 不调用show不会显示系统托盘消息，图标隐藏无法调用

        # 信息提示
        # 参数1：标题
        # 参数2：内容
        # 参数3：图标（0没有图标 1信息图标 2警告图标 3错误图标），0还是有一个小图标
        # noinspection PyTypeChecker
        # self.tp.show_message('Hello', '我藏好了', icon = 0)

        # 绑定托盘菜单点击事件
        self.tp.activated.connect(self.act)